package gestordeentidades;

import entidades.Ancora;
import gestordeentidades.gateways.CorGateway;
import gestordeentidades.gateways.DecoradorGateway;
import gestordeentidades.gateways.EstiloGateway;
import gestordeentidades.gateways.FamiliaGateway;
import gestordeentidades.gateways.PontoGateway;
import gestordeentidades.gateways.RectanguloGateway;
import gestordeentidades.gateways.TelhaGateway;
import gestordeentidades.gateways.TexturaGateway;
import gestordeentidades.gateways.TipoDeTelhaGateway;
import gestordeentidades.identitymap.EntityMap;
import gestordeentidades.identitymap.UnitOfWork;
import gestordeentidades.identitymap.interfaces.IEntityMap;
import gestordeentidades.identitymap.interfaces.IUnityOfWork;
import gestordeentidades.idgenerators.IDgenerator;
import gestordeentidades.idgenerators.IIDgenerator;
import java.util.Map;
import gestordeentidades.persistance.DataBaseObject;
import gestordeentidades.persistance.Persistance;
import gestordeentidades.persistance.database.DataBase;
import gestordeentidades.persistance.interfaces.IDataBaseAdapter;
import gestordeentidades.persistance.interfaces.IPersistance;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
import entidades.Cor;
import entidades.Decorador;
import entidades.DesenhoDinamico;
import entidades.Estilo;
import entidades.Familia;
import entidades.Ponto;
import entidades.Rectangulo;
import entidades.Telha;
import entidades.Textura;
import entidades.TipoTelha;
import gestordeentidades.gateways.AncoraGateway;
import gestordeentidades.gateways.DesenhoDinamicoGateway;

/**
 * Trabalho DAS - 2014/2015
 *
 * @author a21210380, a21190325, a21210392
 */
public class GestorDeEntidades implements IGestor {

    private final IDataBaseAdapter dataBase;
    private final IPersistance persistance;
    private final IEntityMap entityMap;
    private final IUnityOfWork unit;
    private final IIDgenerator idGenerator;

    private final Object lock = new Object();

    private GestorDeEntidades() {
        dataBase = new DataBase();
        idGenerator = new IDgenerator(dataBase)
                .initDataBase()
                .load()
                .iniciarEntidade(Cor.class)
                .iniciarEntidade(Ponto.class)
                .iniciarEntidade(TipoTelha.class)
                .iniciarEntidade(Textura.class)
                .iniciarEntidade(Estilo.class)
                .iniciarEntidade(Familia.class)
                .iniciarEntidade(Rectangulo.class)
                .iniciarEntidade(Telha.class)
                .iniciarEntidade(Decorador.class)
                .iniciarEntidade(DesenhoDinamico.class)
                .iniciarEntidade(Ancora.class);
        
        persistance = new Persistance(dataBase)
                .iniciarEntidade(Cor.class, new CorGateway())
                .iniciarEntidade(Ponto.class, new PontoGateway())
                .iniciarEntidade(TipoTelha.class, new TipoDeTelhaGateway())
                .iniciarEntidade(Textura.class, new TexturaGateway())
                .iniciarEntidade(Estilo.class, new EstiloGateway())
                .iniciarEntidade(Familia.class, new FamiliaGateway())
                .iniciarEntidade(Rectangulo.class, new RectanguloGateway())
                .iniciarEntidade(Telha.class, new TelhaGateway())
                .iniciarEntidade(Decorador.class, new DecoradorGateway())
                .iniciarEntidade(DesenhoDinamico.class, new DesenhoDinamicoGateway())
                .iniciarEntidade(Ancora.class, new AncoraGateway());

        entityMap = new EntityMap(persistance)
                .iniciarEntidade(Cor.class)
                .iniciarEntidade(Ponto.class)
                .iniciarEntidade(TipoTelha.class)
                .iniciarEntidade(Textura.class)
                .iniciarEntidade(Estilo.class)
                .iniciarEntidade(Familia.class)
                .iniciarEntidade(Rectangulo.class)
                .iniciarEntidade(Telha.class)
                .iniciarEntidade(Decorador.class)
                .iniciarEntidade(DesenhoDinamico.class)
                .iniciarEntidade(Ancora.class)
                .loadAll();

        unit = new UnitOfWork(idGenerator, entityMap, persistance)
                .iniciarEntidade(Cor.class)
                .iniciarEntidade(Ponto.class)
                .iniciarEntidade(TipoTelha.class)
                .iniciarEntidade(Textura.class)
                .iniciarEntidade(Estilo.class)
                .iniciarEntidade(Familia.class)
                .iniciarEntidade(Rectangulo.class)
                .iniciarEntidade(Telha.class)
                .iniciarEntidade(Decorador.class)
                .iniciarEntidade(DesenhoDinamico.class)
                .iniciarEntidade(Ancora.class);
    }

    @Override
    public IDataBaseAdapter getDataBaseAdapter() {
        return dataBase;
    }

    @Override
    public IPersistance getPersistance() {
        return persistance;
    }

    @Override
    public <T extends DataBaseObject> boolean registarNovaEntidade(T entidade) {
        synchronized (lock) {
            return unit.registarNovaEntidade(entidade);
        }
    }

    @Override
    public <T extends DataBaseObject> void registarEntidadeModificada(T entidade) {
        synchronized (lock) {
            unit.registarEntidadeModificada(entidade);
        }
    }

    @Override
    public <T extends DataBaseObject> void registarEntidadeApagada(T entidade) {
        synchronized (lock) {
            unit.registarEntidadeApagada(entidade);
        }
    }

    @Override
    public void commit() {
        synchronized (lock) {
            unit.commit();
        }
    }

    @Override
    public void rollback() {
        synchronized (lock) {
            unit.rollback();
        }
    }

    @Override
    public <T extends DataBaseObject> T procurarEntidadePeloID(Class<T> entidade, int id) {
        synchronized (lock) {
            return entityMap.procurarEntidadePeloID(entidade, id);
        }
    }

    @Override
    public <T extends DataBaseObject> Collection<T> procurarPorTodasAsEntidadesComoColecao(Class<T> entidade) {
        synchronized (lock) {
            return entityMap.procurarPorTodasAsEntidadesComoColecao(entidade);
        }
    }

    @Override
    public <T extends DataBaseObject> Map<Integer, T> procurarPorTodasAsEntidadesComoMapa(Class<T> entidade) {
        synchronized (lock) {
            return entityMap.procurarPorTodasAsEntidadesComoMapa(entidade);
        }
    }
    
    @Override
    public <T extends DataBaseObject> List<T> filtrarEntidades(Class<T> entidade, Predicate<T> filtro) {
        synchronized (lock) {
            return entityMap.filtrarEntidades(entidade, filtro);
        }
    }

    @Override
    public <T extends DataBaseObject> int getNextId(Class<T> entidade) {
        return idGenerator.getNextId(entidade);
    }

    private static GestorDeEntidades singleton = null;

    public synchronized static IGestor getInstance() {
        if (singleton == null) {
            singleton = new GestorDeEntidades();
        }
        return singleton;
    }
}
